import CryptoJS from 'crypto-js'
import { ChaCha20Poly1305 } from '@stablelib/chacha20poly1305'
import { streamXOR } from '@stablelib/salsa20'
import IDEA from 'idea-cipher'
// import {TWOFISH} from 'encryption-for-node';
// import { twofish } from 'twofish'
// import { SM4Util } from 'sm4util'
// import { SM4 } from 'gmsm-sm2js'
// // import {Twofish} from 'gnablib'


function intArrayToBase64(intArray) {
    const bytes = new Uint8Array(intArray)
    let binaryString = ''
    for (let i = 0; i < bytes.byteLength; i++) {
        binaryString += String.fromCharCode(bytes[i])
    }
    return btoa(binaryString)
}

function base64ToUint8Array(base64) {
    var binaryString = atob(base64)
    var bytes = new Uint8Array(binaryString.length)
    for (var i = 0; i < binaryString.length; i++) {
        bytes[i] = binaryString.charCodeAt(i)
    }
    return bytes
}

const rc4_encode = (keyObj, message) => {
    // console.log('rc4_encode', keyObj, message, keyObj.key);
    // console.log('rc4_encode1', CryptoJS.RC4.encrypt(message, '123'));
    // console.log('rc4_encode', CryptoJS.RC4.encrypt(message, keyObj.key));
    if (keyObj.code != CryptoCode.RC4) {
        console.log('keyObj.code', keyObj.code, CryptoCode.RC4);

        return 'Crypto mismatch!'
    }
    var key = CryptoJS.enc.Utf8.parse(keyObj.key);
    return CryptoJS.RC4.encrypt(message, key).toString()
}

const rc4_decode = (keyObj, message) => {
    if (keyObj.code != CryptoCode.RC4) {
        return 'Crypto mismatch!'
    }
    var key = CryptoJS.enc.Utf8.parse(keyObj.key);

    const decryptResult = CryptoJS.RC4.decrypt(message, key)
    return decryptResult.toString(CryptoJS.enc.Utf8)
}

const aes_encode = (keyObj, message) => {
    if (keyObj.code != CryptoCode.AES) {
        return 'Crypto mismatch!'
    }
    console.log('aes_encode', keyObj.key, keyObj.iv);

    var key = CryptoJS.enc.Utf8.parse(keyObj.key);
    var iv = CryptoJS.enc.Utf8.parse(keyObj.iv);
    return CryptoJS.AES.encrypt(message, key, { mode: CryptoJS.mode.CBC, iv, padding: CryptoJS.pad.Pkcs7 }).toString()
}

const aes_decode = (keyObj, message) => {
    if (keyObj.code != CryptoCode.AES) {
        return 'Crypto mismatch!'
    }
    var key = CryptoJS.enc.Utf8.parse(keyObj.key);
    var iv = CryptoJS.enc.Utf8.parse(keyObj.iv);
    const decryptResult = CryptoJS.AES.decrypt(message, key, { mode: CryptoJS.mode.CBC, iv, padding: CryptoJS.pad.Pkcs7 })
    return decryptResult.toString(CryptoJS.enc.Utf8)
}
function a() {
    // 加密函数
    function encryptAES(message, keyo, ivo) {
        // 将密钥和IV转换为CryptoJS支持的格式
        var key = CryptoJS.enc.Utf8.parse(keyo);
        var iv = CryptoJS.enc.Utf8.parse(ivo);

        // 使用AES加密
        var encrypted = CryptoJS.AES.encrypt(message, key, {
            iv: iv,
            mode: CryptoJS.mode.CBC,
            padding: CryptoJS.pad.Pkcs7
        });

        // 返回Base64编码的加密结果
        return encrypted.toString();
    }

    // 示例
    var message = "Hello, World!";
    var key = "1234567890123456"; // 16字节的密钥
    var iv = "1234567890123456";  // 16字节的IV

    var encryptedMessage = encryptAES(message, key, iv);
    console.log("Encrypted Message: " + encryptedMessage);
}

const chacha20_encode = (keyObj, message) => {
    var key = keyObj.key;
    var nonce = keyObj.iv;
    if (keyObj.code != CryptoCode.ChaCha20) {
        return 'Crypto mismatch!';
    }

    if (!key || key.length < 32) {
        return 'error: key length must be at least 32 bytes'
    }

    if (nonce && nonce.length > 16) {
        return 'error: nonce length must not exceed 16 bytes'
    }
    const cipher = new ChaCha20Poly1305(new TextEncoder().encode(key))
    const a = cipher.seal(
        new TextEncoder().encode(nonce),
        new TextEncoder().encode(message)
    )
    console.log(a)
    return intArrayToBase64(a)
}
const chacha20_decode = (keyObj, message) => {
    var key = keyObj.key;
    var nonce = keyObj.iv;
    if (keyObj.code != CryptoCode.ChaCha20) {
        return 'Crypto mismatch!'
    }

    if (!key || key.length < 32) {
        return 'error: key length must be at least 32 bytes'
    }

    if (nonce && nonce.length > 16) {
        return 'error: nonce length must not exceed 16 bytes'
    }
    const cipher = new ChaCha20Poly1305(new TextEncoder().encode(key))
    const a = cipher.open(
        new TextEncoder().encode(nonce),
        base64ToUint8Array(message)
    )
    return new TextDecoder().decode(a)
}

const tripledes_encode = (keyObj, message) => {
    var key = CryptoJS.enc.Utf8.parse(keyObj.key);
    var iv = CryptoJS.enc.Utf8.parse(keyObj.iv);
    if (keyObj.code != CryptoCode.TripleDES) {
        return 'Crypto mismatch!'
    }
    return CryptoJS.TripleDES.encrypt(message, key, {
        iv: iv,
        mode: CryptoJS.mode.CBC,
        padding: CryptoJS.pad.Pkcs7
    }).toString()
}

const tripledes_decode = (keyObj, message) => {

    var key = CryptoJS.enc.Utf8.parse(keyObj.key);
    var iv = CryptoJS.enc.Utf8.parse(keyObj.iv);
    if (keyObj.code != CryptoCode.TripleDES) {
        return 'Crypto mismatch!'
    }
    const decryptResult = CryptoJS.TripleDES.decrypt(message, key, {
        iv: iv,
        mode: CryptoJS.mode.CBC,
        padding: CryptoJS.pad.Pkcs7
    })
    return decryptResult.toString(CryptoJS.enc.Utf8)
}

const blowfish_encode = (keyObj, message) => {
    var key = keyObj.key;
    var iv = keyObj.iv;
    if (keyObj.code != CryptoCode.Blowfish) {
        return 'Crypto mismatch!'
    }
    if (iv && iv.length > 8) {
        return 'error: iv length must not exceed 8 bytes'
    }
    key = CryptoJS.enc.Utf8.parse(key)
    iv = CryptoJS.enc.Utf8.parse(iv)
    var ciphertext = CryptoJS.Blowfish.encrypt(message, key, {
        iv: iv,
        mode: CryptoJS.mode.CBC,
        padding: CryptoJS.pad.Pkcs7
    })
    return ciphertext.toString()
}

const blowfish_decode = (keyObj, message) => {
    var key = keyObj.key;
    var iv = keyObj.iv;
    if (keyObj.code != CryptoCode.Blowfish) {
        return 'Crypto mismatch!'
    }
    if (iv && iv.length > 8) {
        return 'error: iv length must not exceed 8 bytes'
    }
    iv = CryptoJS.enc.Utf8.parse(iv)
    key = CryptoJS.enc.Utf8.parse(key)
    var decrypted = CryptoJS.Blowfish.decrypt(message, key, {
        iv: iv,
        mode: CryptoJS.mode.CBC,
        padding: CryptoJS.pad.Pkcs7
    })
    return decrypted.toString(CryptoJS.enc.Utf8)
}

const salsa20_encode = (keyObj, message) => {
    var key = keyObj.key;
    var nonce = keyObj.iv;
    if (keyObj.code != CryptoCode.Salsa20) {
        return 'Crypto mismatch!';
    }

    if (!key || key.length < 32) {
        return 'error: key length must be at least 32 bytes'
    }

    if (nonce && nonce.length > 8) {
        return 'error: nonce length must not exceed 8 bytes'
    }
    var a = streamXOR(
        new TextEncoder().encode(key),
        new TextEncoder().encode(nonce),
        new TextEncoder().encode(message),
        new TextEncoder().encode(message)
    )
    return intArrayToBase64(a)
}
const salsa20_decode = (keyObj, message) => {
    var key = keyObj.key;
    var nonce = keyObj.iv;
    if (keyObj.code != CryptoCode.Salsa20) {
        return 'Crypto mismatch!'
    }

    if (!key || key.length < 32) {
        return 'error: key length must be at least 32 bytes'
    }

    if (nonce && nonce.length > 8) {
        return 'error: nonce length must not exceed 8 bytes'
    }
    const a = streamXOR(
        new TextEncoder().encode(key),
        new TextEncoder().encode(nonce),
        base64ToUint8Array(message),
        base64ToUint8Array(message)
    )
    return new TextDecoder().decode(a)
}

function genRandomKeyObj(len, code, ivLen, seed, seedi) {
    console.log('genRandomKeyObj seed', seed);

    if (seed) {
        return {
            key: seed,
            code: code,
            iv: seedi
        }
    }

    var keyBytes = new Uint8Array(len);
    window.crypto.getRandomValues(keyBytes);
    var iv = undefined
    if (ivLen > 0) {
        iv = new Uint8Array(ivLen);
        window.crypto.getRandomValues(iv);
    }
    return {
        key: intArrayToBase64(keyBytes).slice(0, len),
        code: code,
        iv: intArrayToBase64(iv).slice(0, ivLen)
    }
}


const CryptoCode = {
    RC4: 0,
    AES: 1,
    ChaCha20: 2,
    TripleDES: 3,
    Blowfish: 4,
    Salsa20: 5,
}

const i8 = "12345678";
const i12 = "123456781234";
const i16 = "1234567812345678";

const RC4 = {
    encode: rc4_encode,
    decode: rc4_decode,
    randomKeyObj: (seed) => {
        var seedi = undefined
        if (seed) {
            seed = seed.slice(0, 16)
            // seedi = i8
        }
        return genRandomKeyObj(16, CryptoCode.RC4, 0, seed, seedi)
    }
}
const AES = {
    encode: aes_encode,
    decode: aes_decode,
    randomKeyObj: (seed) => {
        var seedi = undefined
        if (seed) {
            seed = seed.slice(0, 16)
            seedi = i16
        }
        return genRandomKeyObj(16, CryptoCode.AES, 16, seed, seedi)
    }
}
const ChaCha20 = {
    encode: chacha20_encode,
    decode: chacha20_decode,
    randomKeyObj: (seed) => {

        var seedi = undefined
        if (seed) {
            seed = seed.slice(0, 32)
            seedi = i12
        }
        return genRandomKeyObj(32, CryptoCode.ChaCha20, 12, seed, seedi)
    }
}
const TripleDES = {
    encode: tripledes_encode,
    decode: tripledes_decode,
    randomKeyObj: (seed) => {
        var seedi = undefined
        if (seed) {
            seed = seed.slice(0, 24)
            seedi = i8
        }
        return genRandomKeyObj(24, CryptoCode.TripleDES, 8, seed, seedi)
    }
}
const Blowfish = {
    encode: blowfish_encode,
    decode: blowfish_decode,
    randomKeyObj: (seed) => {
        var seedi = undefined
        if (seed) {
            seed = seed.slice(0, 24)
            seedi = i8
        }
        return genRandomKeyObj(24, CryptoCode.Blowfish, 8, seed, seedi)
    }
}
const Salsa20 = {
    encode: salsa20_encode,
    decode: salsa20_decode,
    randomKeyObj: (seed) => {
        var seedi = undefined
        if (seed) {
            seed = seed.slice(0, 32)
            seedi = i8
        }
        return genRandomKeyObj(32, CryptoCode.Salsa20, 8, seed, seedi)
    }
}



class CryptoUtil {
    constructor(code) {
        this.instance = getCrypto(code)
    }
}

function getCrypto(code) {
    switch (Number(code)) {
        case CryptoCode.RC4:
            return RC4;
        case CryptoCode.AES:
            return AES;
        case CryptoCode.ChaCha20:
            return ChaCha20;
        case CryptoCode.TripleDES:
            return TripleDES;
        case CryptoCode.Blowfish:
            return Blowfish;
        case CryptoCode.Salsa20:
            return Salsa20;
    }
}

CryptoUtil.prototype.encode = function (keyObj, message) {
    return this.instance.encode(keyObj, message)
}


CryptoUtil.prototype.decode = function (keyObj, message) {
    return this.instance.decode(keyObj, message)
}


CryptoUtil.prototype.randomKeyObj = function (seed, seedi) {
    return this.instance.randomKeyObj(seed, seedi)
}

CryptoUtil.prototype.encryptText = function (instance, message) {

    var sendCke = instance.sendCke

    var keyObj = {
        key: '1234567812345678',
        code: 1,
        iv: '1234567812345678'
    }
    // keyObj = instance.keyObj
    var encrypted = this.encode(keyObj, message)

    return {
        encrypted,
        sendCke,
        code: instance.code
    }
}

CryptoUtil.prototype.decryptText = function (instance, paramObj) {
    // var instance = context.state.instance
    var keyForKeyObj = instance.keyForKeyObj

    var keyObj;
    // keyObj = this.decode(keyForKeyObj, paramObj.keyEncrypted);
    // keyObj = JSON.parse(keyObj)
    keyObj = {
        key: '1234567812345678',
        code: 1,
        iv: '1234567812345678'
    }
    var content = this.decode(keyObj, paramObj.content)

    console.log('prototype.decryptText', content, paramObj)
    // resolve(content)
    return content
}
CryptoUtil.prototype.toNotEncrypted = function (msgInfo, plainText) {
    msgInfo.encrypted = msgInfo.content
    msgInfo.content = plainText
    msgInfo.codeToShow = msgInfo.code
    delete msgInfo.code
    return msgInfo
}

function convertWordArrayToUint8Array(wordArray) {

    // var arrayOfWords = wordArray.hasOwnProperty("words") ? wordArray.words : [];
    // var length = wordArray.hasOwnProperty("sigBytes") ? wordArray.sigBytes : arrayOfWords.length * 4;

    var arrayOfWords = Object.hasOwn(wordArray, "words") ? wordArray.words : [];
    var length = Object.hasOwn(wordArray, "sigBytes") ? wordArray.sigBytes : arrayOfWords.length * 4;
    var uInt8Array = new Uint8Array(length), index = 0, word, i;

    for (i = 0; i < length; i++) {
        word = arrayOfWords[i];
        uInt8Array[index++] = word >> 24;
        uInt8Array[index++] = (word >> 16) & 0xff;
        uInt8Array[index++] = (word >> 8) & 0xff;
        uInt8Array[index++] = word & 0xff;
    }
    return uInt8Array;
}


CryptoUtil.prototype.encryptFile = function (fileResult, key, file) {
    var wordArray = CryptoJS.lib.WordArray.create(fileResult);

    var encrypted = CryptoJS.AES.encrypt(wordArray, key).toString();

    // 根据实际场景返回
    // return CryptoJS.enc.Base64.stringify(encrypt.ciphertext)

    // var b64 = CryptoJS.enc.Base64.stringify(encrypted.ciphertext)
    var typedArray = convertWordArrayToUint8Array(encrypted);
    console.log('encrypted', encrypted, encrypted.toString(), typedArray);

    // var decrypted = CryptoJS.AES.decrypt(encrypted, key);
    // console.log('encryptFile decrypted', decrypted, key);

    // console.log('reader decrypted', CryptoJS.AES.decrypt(encrypted.toString(), key));

    // console.log('reader decrypted2', CryptoJS.AES.decrypt(CryptoJS.enc.Base64.parse(CryptoJS.enc.Base64.stringify(encrypted.ciphertext)), key));

    var fileEnc = new Blob([encrypted]);
    var resFile = new File([fileEnc], file.name, { type: file.type/*'image/png'*/ });

    // var reader = new FileReader();
    // reader.readAsText(resFile);
    // reader.onload = () => {
    //     console.log('reader result', reader.result.toString());
    //     // console.log('reader', CryptoJS.enc.Base64.parse(reader.result));
    //
    //     var f = CryptoJS.AES.decrypt(reader.result, key);
    //
    //     console.log('reader decrypted', f);
    // }

    return resFile;
}

CryptoUtil.prototype.encryptFileImpl = function (fileResult, key, filemeta) {
    var wordArray = CryptoJS.lib.WordArray.create(fileResult);

    var encrypted = CryptoJS.AES.encrypt(wordArray, key).toString();

    // 根据实际场景返回
    // return CryptoJS.enc.Base64.stringify(encrypt.ciphertext)

    // var b64 = CryptoJS.enc.Base64.stringify(encrypted.ciphertext)
    var typedArray = convertWordArrayToUint8Array(encrypted);
    console.log('encrypted', encrypted, encrypted.toString(), typedArray);

    // var decrypted = CryptoJS.AES.decrypt(encrypted, key);
    // console.log('encryptFile decrypted', decrypted, key);

    // console.log('reader decrypted', CryptoJS.AES.decrypt(encrypted.toString(), key));

    // console.log('reader decrypted2', CryptoJS.AES.decrypt(CryptoJS.enc.Base64.parse(CryptoJS.enc.Base64.stringify(encrypted.ciphertext)), key));

    var fileEnc = new Blob([encrypted]);
    var resFile = new File([fileEnc], filemeta.name, { type: filemeta.type/*'image/png'*/ });

    // var reader = new FileReader();
    // reader.readAsText(resFile);
    // reader.onload = () => {
    //     console.log('reader result', reader.result.toString());
    //     // console.log('reader', CryptoJS.enc.Base64.parse(reader.result));
    //
    //     var f = CryptoJS.AES.decrypt(reader.result, key);
    //
    //     console.log('reader decrypted', f);
    // }

    return resFile;
}

CryptoUtil.prototype.encryptFileReal = function (instance, file) {
    return new Promise((resolve, reject) => {
        var code = 0
        var keyObj = {
            code,
            key: '12345678'
        }
        var reader = new FileReader();
        reader.readAsArrayBuffer(file);
        var filemeta = {
            name: file.name,
            type: file.type,
        }
        reader.onload = () => {
            console.log('reader', reader.result);
            var fileEncrypted = new CryptoUtil(code).encryptFileImpl(reader.result, keyObj.key, filemeta)
            resolve(fileEncrypted)
        }
    });
}

CryptoUtil.prototype.decryptFileReal = function (instance, url) {
    // var url = paramObj.url
    return new Promise((resolve, reject) => {let accessToken = sessionStorage.getItem("accessToken");
        fetch(url, {headers: {accessToken}}).then(res => {
            //
            const header = res.headers.get('content-type');
            console.log('fetch res', res, header)
            // const parts = header.split(';');
            // var filename = parts[1].split('=')[1];
            res.blob().then(file => {
                var code = 0
                var keyObj = {
                    code,
                    key: '12345678'
                }
                console.log('fetch', file, url.split('/').at(-1));
                // var filemeta = {
                //     name: file.name,
                //     type: file.type,
                // }
                var filemeta = {
                    name: url.split('/').at(-1),
                    type: res.headers.get('content-type'),
                    // type: 'image/png',
                }
                var reader = new FileReader();
                reader.readAsText(file);
                reader.onload = () => {
                    console.log('reader.result', reader.result);
                    var decryptedFile = new CryptoUtil(code).decryptFile(reader.result, keyObj.key, filemeta);
                    console.log('decryptedFile', decryptedFile)
                    resolve(decryptedFile)
                }
            }).catch(e=>{
                console.log(e);
                resolve(null)
            })
            // return res.blob()
        })
    });
}

CryptoUtil.prototype.decryptFile = function (fileResult, key, file) {
    var decrypted = CryptoJS.AES.decrypt(fileResult, key);               // Decryption: I: Base64 encoded string (OpenSSL-format) -> O: WordArray
    console.log('decrypted', decrypted, key);
    if (!file) {
        file = {
            name: 'test.png',
            type: 'image/png',
        }
    }

    var typedArray = convertWordArrayToUint8Array(decrypted);               // Convert: WordArray -> typed array

    var fileDec = new Blob([typedArray]);

    return new File([fileDec], file.name, { type: file.type })
}

CryptoUtil.prototype.i16 = '1234567890123456'
CryptoUtil.prototype.i12 = '123456789012'
CryptoUtil.prototype.i8 = '12345678'

CryptoUtil.prototype.testAES = function () {
    // 加密函数
    function encryptAES(message, keyo, ivo) {
        // 将密钥和IV转换为CryptoJS支持的格式
        var key = CryptoJS.enc.Utf8.parse(keyo);
        var iv = CryptoJS.enc.Utf8.parse(ivo);
        console.log('aes_encode', key, iv);
        // 使用AES加密
        var encrypted = CryptoJS.AES.encrypt(message, key, {
            iv: iv,
            mode: CryptoJS.mode.CBC,
            padding: CryptoJS.pad.Pkcs7
        });

        // 返回Base64编码的加密结果
        return encrypted.toString();
    }

    // 示例
    var message = "Hello, World!";
    var key = "1234567890123456"; // 16字节的密钥
    var iv = "1234567890123456";  // 16字节的IV

    var encryptedMessage = encryptAES(message, key, iv);
    console.log("Encrypted Message: " + encryptedMessage);
}

CryptoUtil.prototype.testsal = function () {
    var key = "12345678123456781234567812345678"
    var nonce = "12345678"
    var message = "123467812345678"
    var a = streamXOR(
        new TextEncoder().encode(key),
        new TextEncoder().encode(nonce),
        new TextEncoder().encode(message),
        new TextEncoder().encode(message)
    )
    console.log(a, intArrayToBase64(a))
    a = streamXOR(
        new TextEncoder().encode(key),
        new TextEncoder().encode(nonce),
        a,
        new TextEncoder().encode(message)
    )

    console.log(a, new TextDecoder().decode(a))
}

function toNotEncrypted(msgInfo, plainText) {
    msgInfo.encrypted = msgInfo.content
    msgInfo.content = plainText
    msgInfo.codeToShow = msgInfo.code
    delete msgInfo.code
    return msgInfo
}

export { RC4, AES, ChaCha20, TripleDES, Blowfish, Salsa20, toNotEncrypted, CryptoUtil }